#include "UtilCommon.h"
#include <dbghelp.h>

#pragma comment(lib, "dbghelp.lib")

CThreadPool ThreadPool;

void CControlInterface::FreezeThread()
{
	if(m_threadid != GetCurrentThreadId())
		return;
	SuspendThread(hThread);
}

void CControlInterface::RestartThread()
{
	if(m_threadid == GetCurrentThreadId())
		return;
	ResumeThread(hThread);
}

CThreadPool::CThreadPool()
{
}

void CThreadPool::ExecuteTask(CThread* tr)
{
	if(m_freethreads.size())
	{
		m_freelock.Acquire();
		Thread* t = m_freethreads.front();
		m_freethreads.pop_front();
		t->Execute = tr;
		t->control.RestartThread();
		m_freelock.Release();
	}
	else
		StartThread(tr);
}

void CThreadPool::Start()
{
	m_freelock.Acquire();
	for(register ui32 i = 0; i < USABLE_THREADS; ++i)
	{
		m_freethreads.push_back(StartThread(0));
	}
	m_freelock.Release();
}

static int dumpcount = 0;

int HandleException(ui32 excode, EXCEPTION_POINTERS* ep)
{
	MINIDUMP_EXCEPTION_INFORMATION info;
	info.ClientPointers = FALSE;
	info.ExceptionPointers = ep;
	info.ThreadId = GetCurrentThreadId();

	char path[MAX_PATH];
	sprintf(path, "dump%u.dmp", dumpcount++);
	HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
	if(hFile == INVALID_HANDLE_VALUE)
		return 1;

	MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithIndirectlyReferencedMemory, &info, 0, 0);
	CloseHandle(hFile);
	return 1;
}

enum CrashResult
{
	CONTINUE_HANDLING		= 0x00,
	RESTART_THREAD			= 0x01,
};

CrashResult Reanimate(Thread* threadinfo)
{
	try
	{
		CThread* polymorphic = threadinfo->Execute;
		if(polymorphic)
		{
			const char* type = typeid(*polymorphic).name();
			if(strstr(type, "Session"))
			{
				Session* sess = (Session*)polymorphic;
				sess->Disconnect();
			}
			else if(strstr(type, "MapHandler"))
			{
				MapHandler* handler = (MapHandler*)polymorphic;
				handler->TryReanimate();
				return RESTART_THREAD;
			}
		}
	}
	catch(...)
	{
	}
	return CONTINUE_HANDLING;
}

static unsigned long WINAPI proc_addr(void* param)
{
	Thread* t = (Thread*)param;
	if(!t)
		ExitThread(0);
start:
	__try
	{
		for( ; ; )
		{
			if(t->Execute != NULL)
			{
				t->Execute->Loop();
				delete t->Execute;
				if(t->Kill)
					break;
				t->Execute = NULL;
				ThreadPool.m_freelock.Acquire();
				ThreadPool.m_freethreads.push_back(t);
				ThreadPool.m_freelock.Release();
			}
			if(ThreadPool.IsExiting(t))
				break;
			t->control.FreezeThread();

		}
	}
	__except(HandleException(GetExceptionCode(), GetExceptionInformation()))
	{
		if(t)
		{
			switch(Reanimate(t))
			{
			case RESTART_THREAD:
				goto start;
				break;
			}
		}
	}
	ExitThread(0);
}

Thread* CThreadPool::StartThread(CThread* execbase)
{
	Thread* t = new Thread();
	t->Execute  = execbase;
	t->Kill = false;
	execbase->ThreadState = 1;
	DWORD id;
	HANDLE hThread = CreateThread(NULL, 0, &proc_addr, (void*)t, 0, &id);
	t->control.m_threadid = id;
	t->control.RegisterThread(hThread);
	return t;
}

void CThreadPool::ShutDown()
{
	set<Thread*>::iterator itr = m_activethreads.begin();
	set<Thread*>::iterator end = m_activethreads.end();
	for( ; itr != m_activethreads.end(); ++itr)
	{
		m_threadstoexit.insert(*itr);
		(*itr)->Execute->ThreadState = 0;
		(*itr)->Execute = NULL;
		(*itr)->Kill = true;
	}
}

void CThreadPool::RemoveFreeThread(Thread* t)
{
	Log->Notice("ThreadPool", "Thread %u left threadpool", t->control.m_threadid);
	if(IsExiting(t))
	{
		m_threadstoexit.erase(t);
	}
}

void CThreadPool::AddActiveThread(Thread* t)
{
	m_activelock.Acquire();
	m_activethreads.insert(t);
	m_activelock.Release();
}

void CThreadPool::RemoveActiveThread(Thread* t)
{
	m_activelock.Acquire();
	m_activethreads.erase(t);
	m_activelock.Release();
}

void CThreadPool::SanityCheck()
{
	m_freelock.Acquire();
	Log->Notice("ThreadPool", "=====================================");
	Log->Notice("ThreadPool", "|           Sanitycheck:            |");
	Log->Notice("ThreadPool", "|        Free Threads: %02u           |", m_freethreads.size());
	Log->Notice("ThreadPool", "=====================================");
	m_freelock.Release();
}